using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Text;
using System.Xml;
using SSP.Api.Options;

namespace SSP.Api
{
    /// <summary>
    /// 
    /// </summary>
    [Serializable]
    public class Director
    {
        private readonly List<ImageOptions> sizes = new List<ImageOptions>();
        private readonly List<UserOptions> user_sizes = new List<UserOptions>();
        private List<String> post = new List<String>();

        /// <summary>
        /// Initializes a new instance of the <see cref="Director"/> class.
        /// </summary>
        /// <param name="api_key">The api_key.</param>
        /// <param name="path">The path.</param>
        public Director(string api_key, string path)
        {
            //var matches = new OrderedMap();
            //int offset;
            //string doc_root;

            if (!String.IsNullOrEmpty(api_key))
            {
                ApiKey = api_key;
            }

            if (!String.IsNullOrEmpty(path))
            {
                Path = path;
            }

            ApiKey = ApiKey.Trim(new[] {' ', '\t', '\n', '\r'});
            Path = Path.TrimEnd("/".ToCharArray()).Replace("http://", "");

            if (String.IsNullOrEmpty(ApiKey) || String.IsNullOrEmpty(Path))
            {
                throw new Exception("You must specify an API key and install path.");
            }

            int posOfHyphen = ApiKey.IndexOf("-") + 1;

            if (posOfHyphen > 0)
            {
                if (ApiKey.StartsWith("local"))
                    is_local = true;

                ApiKey = ApiKey.Substring(posOfHyphen);
            }
            else
            {
                throw new Exception("Invalid API key.");
            }

            endpoint = "http://" + Path;
            if (is_local)
            {
                endpoint += "/index.php?";
            }
            endpoint += "/api/";

            //cache_path = Path.GetDirectoryName(__FILE__) + Convert.ToString(Path.DirectorySeparatorChar) + "cache" + Convert.ToString(Path.DirectorySeparatorChar);
            //offset = 0;
            //if (!Convert.ToBoolean(HttpContext.Current.Request.CurrentExecutionFilePath.IndexOf(".php")))
            //{
            //    offset = 4;
            //}
            //doc_root = HttpContext.Current.Request.PhysicalPath.Substring(0, HttpContext.Current.Request.PhysicalPath.Length - (HttpContext.Current.Request.CurrentExecutionFilePath.Length + offset));
            //cache_invalidator = TypeSupport.ToString(StringSupport.StringReplace(TypeSupport.ToString(HttpContext.Current.Request.Headers["HOST"]) + TypeSupport.ToString(StringSupport.StringReplace(StringSupport.StringReplace(__FILE__, doc_root, ""), Convert.ToString(Path.DirectorySeparatorChar), "/")), "DirectorPHP.php", "cache/clear.php"));
            App = new DirectorApp(this);
            Format = new DirectorFormat(this);
            Gallery = new DirectorGallery(this);
            Album = new DirectorAlbum(this);
            Content = new DirectorContent(this);
            Utils = new DirectorUtils(this);
            Cache = new DirectorCache(this);
            User = new DirectorUser(this);
            cache_duration = new TimeSpan(1,0,0);
        }

        /// <summary>
        /// Gets or sets the __ FIL e__.
        /// </summary>
        /// <value>The __ FIL e__.</value>
        internal string __FILE__ { get; set; }

        /// <summary>
        /// Gets or sets the album.
        /// </summary>
        /// <value>The album.</value>
        public DirectorAlbum Album { get; set; }

        /// <summary>
        /// Gets or sets the api_key.
        /// </summary>
        /// <value>The api_key.</value>
        public string ApiKey { get; set; }

        /// <summary>
        /// Gets or sets the app.
        /// </summary>
        /// <value>The app.</value>
        public DirectorApp App { get; set; }

        /// <summary>
        /// Gets or sets the cache.
        /// </summary>
        /// <value>The cache.</value>
        public DirectorCache Cache { get; set; }

        /// <summary>
        /// Gets or sets the cache_invalidator.
        /// </summary>
        /// <value>The cache_invalidator.</value>
        internal string cache_invalidator { get; set; }

        /// <summary>
        /// Gets or sets the cache_key.
        /// </summary>
        /// <value>The cache_key.</value>
        internal string cache_key { get; set; }

        /// <summary>
        /// Gets or sets the cache_path.
        /// </summary>
        /// <value>The cache_path.</value>
        internal string cache_path { get; set; }

        /// <summary>
        /// Gets or sets the content.
        /// </summary>
        /// <value>The content.</value>
        public DirectorContent Content { get; set; }

        /// <summary>
        /// Gets or sets the endpoint.
        /// </summary>
        /// <value>The endpoint.</value>
        internal string endpoint { get; set; }

        /// <summary>
        /// Gets or sets the format.
        /// </summary>
        /// <value>The format.</value>
        public DirectorFormat Format { get; set; }

        /// <summary>
        /// Gets or sets the gallery.
        /// </summary>
        /// <value>The gallery.</value>
        public DirectorGallery Gallery { get; set; }

        /// <summary>
        /// Gets or sets a value indicating whether this <see cref="Director"/> is is_local.
        /// </summary>
        /// <value><c>true</c> if is_local; otherwise, <c>false</c>.</value>
        internal bool is_local { get; set; }

        /// <summary>
        /// Gets or sets the path.
        /// </summary>
        /// <value>The path.</value>
        public string Path { get; set; }

        /// <summary>
        /// Gets or sets the preview.
        /// </summary>
        /// <value>The preview.</value>
        internal PreviewOptions preview { get; set; }

        /// <summary>
        /// Gets or sets a value indicating whether this <see cref="Director"/> is to_cache.
        /// </summary>
        /// <value><c>true</c> if to_cache; otherwise, <c>false</c>.</value>
        internal bool to_cache { get; set; }

        /// <summary>
        /// Gets or sets the user.
        /// </summary>
        /// <value>The user.</value>
        public DirectorUser User { get; set; }

        /// <summary>
        /// Gets or sets the user_scope.
        /// </summary>
        /// <value>The user_scope.</value>
        internal UserScope user_scope { get; set; }

        /// <summary>
        /// Gets or sets the utils.
        /// </summary>
        /// <value>The utils.</value>
        public DirectorUtils Utils { get; set; }

        /// <summary>
        /// Gets the post.
        /// </summary>
        /// <value>The post.</value>
        internal List<string> Post
        {
            get { return post; }
        }

        /// <summary>
        /// Gets or sets the sizes.
        /// </summary>
        /// <value>The sizes.</value>
        internal List<ImageOptions> Sizes
        {
            get { return sizes; }
        }

        /// <summary>
        /// Gets or sets the user sizes.
        /// </summary>
        /// <value>The user sizes.</value>
        public List<UserOptions> UserSizes
        {
            get { return user_sizes; }
        }

        internal TimeSpan cache_duration { get; set; }

        /********************************************************
		
		public functions
		
		Only methods in this class or a subclass of
		this class can call these methods.
		
		********************************************************/

        /// <summary>
        /// Format_sizeses this instance.
        /// </summary>
        protected void format_sizes()
        {
            for (int i = 0; i < sizes.Count; i++)
            {
                post.Add(string.Format("data[size][{0}]={1}", i, sizes[i]));
            }

            if (preview != null)
            {
                post.Add("data[preview]=" + preview);
            }


            for (int i = 0; i < user_sizes.Count; i++)
            {
                post.Add(string.Format("data[user_size][{0}]={1}", i, user_sizes[i]));
            }
        }


        /// <summary>
        /// Sends the specified method.
        /// </summary>
        /// <param name="method">The method.</param>
        /// <param name="tail">The tail.</param>
        /// <returns></returns>
        public DirectorResponse send(string method, string tail)
        {
            string ret = "";
            bool cache = false;
            if (Cache != null && !String.IsNullOrEmpty(cache_key))
            {
                cache = true;
            }
            if (cache)
            {
                ret = Cache.Get(tail);
            }
            if (String.IsNullOrEmpty(ret))
            {
                if (cache)
                {
                    post.Add("data[invalidator][path]=" + cache_invalidator);
                    post.Add("data[invalidator][name]=" + cache_key + "/" + tail);
                }
                format_sizes();
                post.Add("data[api_key]=" + ApiKey);
                string post_str = String.Join("&", post.ToArray());
                post = new List<string>();

                try
                {
                    ret = HttpPost(endpoint + method, post_str);
                }
                catch (WebException e)
                {
                    throw e;
                }

                if (cache)
                {
                    Cache.Fill(ret, tail);
                }
            }
            DirectorResponse response = simplexml_load_string(ret);
            if (! String.IsNullOrEmpty(response.Error))
            {
                throw new Exception("SlideShowPro Director Error: " + response.Error);
            }
            if (Cache == null)
            {
                Cache = new DirectorCache(this);
            }
            return response;
        }

        private DirectorResponse simplexml_load_string(string ret)
        {
            TextReader str = new StringReader(ret);
            var settings = new XmlReaderSettings {IgnoreWhitespace = true, IgnoreComments = true, IgnoreProcessingInstructions = true};
            XmlReader xreader = XmlReader.Create(str, settings);
            xreader.Read();
            xreader.MoveToContent();
            var res = new DirectorResponse(this);
            res.DeserializeFromReader(xreader);
            return res;
        }

        /// <summary>
        /// Sends the specified s.
        /// </summary>
        /// <param name="s">The s.</param>
        /// <returns></returns>
        public DirectorResponse send(string s)
        {
            return send(s, "");
        }

        private string HttpPost(string uri, string parameters)
        {
            var webRequest = (HttpWebRequest) WebRequest.Create(uri);
            webRequest.ContentType = "application/x-www-form-urlencoded";
            webRequest.Method = "POST";
            webRequest.Accept = "text/xml";
            byte[] bytes = Encoding.ASCII.GetBytes(parameters);
            Stream os = null;
            try
            {
                // send the Post
                webRequest.ContentLength = bytes.Length; //Count bytes to send
                os = webRequest.GetRequestStream();
                os.Write(bytes, 0, bytes.Length); //Send it
            }
            catch (WebException e)
            {
                // TODo Log e
                throw e;
            }
            finally
            {
                if (os != null)
                {
                    os.Close();
                }
            }

            // Get the response
            WebResponse webResponse = webRequest.GetResponse();
            if (webResponse == null)
            {
                return null;
            }
            var sr = new StreamReader(webResponse.GetResponseStream());
            return sr.ReadToEnd().Trim();
        } // end HttpPost 
    }
}